package log

import (
	"bytes"
	"code.google.com/p/gochess/notation/fen"
	"code.google.com/p/gochess/notation/lan"
	pos "code.google.com/p/gochess/position"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
)

type Logger interface {
	Start(*pos.Position, int32)
	Stop()
	EnterIteration(int32)
	LeaveIteration()
	EnterSearch(*pos.Position, pos.Move)
	LeaveSearch()
	Print(...interface{})
	Printf(string, ...interface{})
}

func NewFileSystemTreeLogger(logPath string) *FileSystemTreeLogger {
	return &FileSystemTreeLogger{currentRoot: logPath}
}

// FileSystemTreeLogger stores the moves and infos as a tree into the file system.
type FileSystemTreeLogger struct {
	start        pos.Position
	currentRoot  string
	currentDepth int32
	currentMove  []int32
	info         []bytes.Buffer
}

func (this *FileSystemTreeLogger) Start(position *pos.Position, maxDepth int32) {

	this.currentDepth = 0
	this.info = make([]bytes.Buffer, maxDepth+1)
	this.currentMove = make([]int32, maxDepth+1)
	this.Printf("fen=%s", fen.Sprint(position))

}

func (this *FileSystemTreeLogger) Stop() {
	this.writeValues()
}

func (this *FileSystemTreeLogger) EnterIteration(i int32) bool {
	this.currentRoot = fmt.Sprintf("%s/%d", this.currentRoot, i)
	if err := os.Mkdir(this.currentRoot, 0755); err != nil {
		panic(err)
	}
	this.currentDepth++
	this.info[this.currentDepth].Reset()
	for i := range this.currentMove {
		this.currentMove[i] = 0
	}
	return false
}

func (this *FileSystemTreeLogger) LeaveIteration() {
	this.writeValues()
	this.currentRoot = path.Dir(this.currentRoot)
	this.currentDepth--
}

func (this *FileSystemTreeLogger) EnterSearch(p *pos.Position, move pos.Move) {
	this.currentMove[this.currentDepth]++
	this.currentRoot = fmt.Sprintf("%s/%02d. %s", this.currentRoot,
		this.currentMove[this.currentDepth], lan.SprintMove(p, move))
	if err := os.Mkdir(this.currentRoot, 0755); err != nil {
		panic(err)
	}
	this.currentDepth++
	this.info[this.currentDepth].Reset()
}

func (this *FileSystemTreeLogger) LeaveSearch() {
	this.writeValues()
	this.currentMove[this.currentDepth] = 0
	this.currentRoot = path.Dir(this.currentRoot)
	this.currentDepth--
}

func (this *FileSystemTreeLogger) Print(a ...interface{}) {
	fmt.Fprint(&this.info[this.currentDepth], a...)
	this.info[this.currentDepth].WriteString("\n")
}

func (this *FileSystemTreeLogger) Printf(format string, a ...interface{}) {
	fmt.Fprintf(&this.info[this.currentDepth], format, a...)
	this.info[this.currentDepth].WriteString("\n")
}

func (this *FileSystemTreeLogger) writeValues() {

	if err := ioutil.WriteFile(this.currentRoot+"/info",
		this.info[this.currentDepth].Bytes(), 0644); err != nil {
		panic(err)
	}
}

type FileLogger struct {
	file   *os.File
	logger log.Logger
}

func NewFileLogger(filename string) *FileLogger {
	file, err := os.Create(filename)
	if err != nil {
		panic(err.Error())
	}
	return &FileLogger{file: file, logger: *log.New(file, "", 0)}
}

func (this *FileLogger) Start(p *pos.Position) {
	this.Println(fen.Sprint(p))
}

func (this *FileLogger) Stop() {
	err := this.file.Close()
	if err != nil {
		panic(err.Error())
	}
}

const iidIndent = "       "

func (this *FileLogger) EnterIteration(i int32) bool {
	this.Printf("iterative deepening depth %d start\n", i)
	return false
}

func (this *FileLogger) LeaveIteration(i int32) {
	this.Printf("iterative deepening depth %d done\n", i)
}

const sIndent = "n                                                                      "

func (this *FileLogger) EnterSearch(p *pos.Position, move pos.Move, start bool) bool {
	if !start {
		this.logger.Printf("%s plays %v", p.SideToMove(), sprintMove(p, move))
	}
	this.logger.SetPrefix(sIndent[:len(this.logger.Prefix())+2])
	return false
}

func (this *FileLogger) LeaveSearch(stop bool) bool {
	if stop {
		this.logger.SetPrefix(iidIndent[:len(this.logger.Prefix())-2])
	} else {
		this.logger.SetPrefix(sIndent[:len(this.logger.Prefix())-2])
	}
	return false
}

const qIndent = "q                                                                      "

func (this *FileLogger) EnterQuiescence(p *pos.Position, move pos.Move, start bool) bool {
	if !start {
		this.logger.Printf("%s plays %v", p.SideToMove(), sprintMove(p, move))
	}
	this.logger.SetPrefix(qIndent[:len(this.logger.Prefix())+2])
	return false
}

func (this *FileLogger) LeaveQuiescence(stop bool) bool {
	if stop {
		this.logger.SetPrefix(sIndent[:len(this.logger.Prefix())-2])
	} else {
		this.logger.SetPrefix(qIndent[:len(this.logger.Prefix())-2])
	}
	return false
}

func (this *FileLogger) Print(a ...interface{}) bool {
	this.logger.Print(a...)
	return false
}

func (this *FileLogger) Println(s string) bool {
	this.logger.Println(s)
	return false
}

func (this *FileLogger) Printf(format string, a ...interface{}) bool {
	this.logger.Printf(format, a...)
	return false
}

func sprintMove(p *pos.Position, move pos.Move) string {
	if move == pos.NoMove {
		return "NoMove"
	}
	return lan.SprintMove(p, move)
}
